package com.mzm.remoting;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mzm.api.MzmAdminFacade;
import com.mzm.remoting.invoker.Authenticator;

public class DelegatedMzmServices implements ExceptionAwareMzmServices {

	private static final Logger logger = LoggerFactory.getLogger(DelegatedMzmServices.class);

	private IMzmServices delegate;

	private MzmAdminFacade mzmAdminFacade;

	public void connectTo(ConnectionMethod connectionMethod, String host) {
		switch (connectionMethod) {
		case HTTP_INVOKER:
			delegate = MzmServices.getHttpInvokerServices(host);
			break;

		default:
			throw new UnsupportedOperationException();
		}
	}

	public void setDelegate(IMzmServices delegate) {
		this.delegate = delegate;
	}

	@Override
	public Object getRemoteObject(String url, Class clax) {
		return delegate.getRemoteObject(url, clax);
	}

	@Override
	public MzmAdminFacade getMzmAdminFacade() {
		if (mzmAdminFacade == null) {
			MzmAdminFacade maf = delegate.getMzmAdminFacade();
			mzmAdminFacade = (MzmAdminFacade) Proxy.newProxyInstance(maf.getClass()
					.getClassLoader(), new Class[] { MzmAdminFacade.class },
					new AuthenticationHandler(maf));
		}
		return mzmAdminFacade;
	}
	
	@Override
	public Object getGenericService(String serviceId, Class<?> serviceInterface) {
		Object genericService = delegate.getGenericService(serviceId, serviceInterface);
		return Proxy.newProxyInstance(genericService.getClass().getClassLoader(),
				new Class[] { serviceInterface }, new AuthenticationHandler(genericService));
	}

	private class AuthenticationHandler implements InvocationHandler {

		private Object delegate;

		private Authenticator authenticator = new Authenticator();

		public AuthenticationHandler(Object delegate) {
			this.delegate = delegate;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			try {
				Method delegatedMethod = delegate.getClass().getMethod(method.getName(),
						method.getParameterTypes());
				authenticator.authenticateUsingConfiguration();
				return delegatedMethod.invoke(delegate, args);
			} catch (InvocationTargetException e) {
				fireExceptionReport(e.getTargetException());
				return null;
			} catch (Exception e) {
				fireExceptionReport(e);
				return null;
			}
		}
	}

	private void fireExceptionReport(Throwable t) {
		if (exceptionListener == null) {
			logger.error("{}", t.getMessage());
			if(t.getCause() != null){
				logger.error("{}", t.getCause().getMessage());
			}
		} else {
			exceptionListener.exceptionReported((Exception) t);
		}
	}

	@Override
	public void registerExceptionListener(ExceptionListener exceptionListener) {
		this.exceptionListener = exceptionListener;
	}

	private ExceptionListener exceptionListener;

}
